import buildURL from 'axios/unsafe/helpers/buildURL.js'
import { httpBaseUrl, httpDevUrl } from '@/service'
import axios from 'axios'
import type { AxiosRequestConfig, AxiosRequestHeaders,AxiosResponse,InternalAxiosRequestConfig } from 'axios'
import qs from 'qs'
import type { ParamsSerializer, IResponseData } from './type'
import { connectSocket } from '@/service/socket'
import { useSystemStore, useUserStore } from '@/store'
import { userApi } from './index'
import { gotoPage } from '@/common/utils'

function getFullURL(
  baseURL: string,
  url: string,
  params: Record<string, any>,
  paramsSerializer?: ParamsSerializer
): string {
  if (url.startsWith('http')) {
    return buildURL(url, params, paramsSerializer)
  }
  baseURL = baseURL.endsWith('/') ? baseURL : `${baseURL}/`
  url = url.startsWith('/') ? url.slice(1) : url
  return buildURL(`${baseURL}${url}`, params, paramsSerializer)
}

const instance = axios.create({
  baseURL: uni.getStorageSync('DEV_ENV') ? httpDevUrl : httpBaseUrl,
  adapter: (config:any) => {
    const {url, method, data, params, headers, baseURL, paramsSerializer, responseType} = config
    return new Promise((resolve, reject) => {
      uni.request({
        method: method!.toUpperCase() as any,
        url: getFullURL(uni.getStorageSync('DEV_ENV') ? httpDevUrl : httpBaseUrl, url!, params, paramsSerializer),
        header:{
          ...headers
        },
        data,
        responseType: responseType,
        success: (res: any) => {
          res.config = config
          resolve(res)
        },
        fail: (err: any) => {
          err.config = config
          reject(err)
        }
      })
    })
  }
})

//请求拦截器
instance.interceptors.request.use((config:InternalAxiosRequestConfig) => {
  const {method} = config
  const {token} = useUserStore()
  const headers: AxiosRequestHeaders = config.headers ?? {}
  if (token.accessToken) {
    headers['etoken'] = token.accessToken
  }
  if (method === 'get' || method === 'delete') {
    headers['Cache-Control'] = 'no-cache'
    headers['Content-type'] = 'application/x-www-form-urlencoded'
  } else if (method === 'post' || method === 'put') {
    headers['Content-type'] = 'application/json'
  }
  if (typeof headers['Content-type'] === 'string' && headers['Content-type'].includes('application/x-www-form-urlencoded')) {
    config.data = qs.stringify(config.data)
  }
  return {
    ...config,
    headers
  }
})

// 是否正在刷新Token
let isRefreshing = false
// 重试队列
const retryRequests: (() => void)[] = []

// 重试请求
const retryRequest = (config: AxiosRequestConfig) => {
  const {token} = useUserStore()
  if (token.accessToken && config?.headers) {
    config.headers['etoken'] = token.accessToken
    console.log('[HTTP]', '重试请求', config.url)
    return instance(config)
  } else {
    return Promise.reject('[HTTP] 重试请求失败');
  }
}

//响应拦截器
instance.interceptors.response.use(async (res:AxiosResponse) => {
  console.log(res)
  const systemStore = useSystemStore()
  const userStore = useUserStore()
  //@ts-ignore
  const responseStatus = res.status || res.statusCode
  if (responseStatus === 200) {
    // 如果存在token信息，刷新状态以及缓存
    if (res.data?.data?.accessToken && res.data?.data?.refreshToken) {
      // 响应数据中存在AccessToken
      userStore.setAccessToken(res.data.data.accessToken, res.data.data.expireIn)
      uni.setStorageSync('REFRESH_TOKEN', res.data.data.refreshToken)
      uni.setStorageSync('REFRESH_TOKEN_EXPIRE_IN', res.data.data.refreshTokenExpireIn)
    }
    if (systemStore.socketStatus !== 1 && userStore.userInfo) {
      connectSocket().then(() => {
      })
    }
    return res
  } else if (responseStatus === 401) {
    //3002 TOKEN过期  3003 TOKEN不合法
    if ([2001, 3002, 3003].includes(res.data.code)) {
      const refreshToken = uni.getStorageSync('REFRESH_TOKEN')
      if (!!refreshToken) {
        const refreshTokenExpireIn = uni.getStorageSync('REFRESH_TOKEN_EXPIRE_IN')
        // refreshToken失效不重试
        if (!!refreshTokenExpireIn && refreshTokenExpireIn < new Date().getTime()) {
          console.log('[HTTP]', 'REFRESH_TOKEN过期')
          userStore.logOut()
          await gotoPage('/pages/login/login')
          return res
        }
        if (!isRefreshing) {
          isRefreshing = true
          await userApi.refreshToken({refreshToken}).then(async () => {
            await retryRequest(res.config).then((retryRes: any) => {
              res = retryRes
            })
          }).catch(err => {
            // 刷新失败 跳转登录
            userStore.logOut()
            gotoPage('/pages/login/login')
            return Promise.reject(err)
          }).finally(() => {
            isRefreshing = false
            retryRequests.forEach(cb => cb())
          })
        } else {
          return new Promise((resolve) => {
            retryRequests.push(() => resolve(retryRequest(res.config)))
          })
        }
      } else await gotoPage('/pages/login/login')
      return res
    }
  }
  return Promise.reject(res)
}, (error: any) => {
  uni.showToast({
    title: '网络异常',
    icon: 'error',
    duration: 3000
  })
  return error
})

const httpPack = {
  get: <P, R>(url: string, param?: P, config?: AxiosRequestConfig): Promise<IResponseData<R>> => httpNative.get<P, IResponseData<R>>(url, param, config),
  delete: <P, R>(url: string, param?: P, config?: AxiosRequestConfig): Promise<IResponseData<R>> => httpNative.delete<P, IResponseData<R>>(url, param, config),
  post: <P, R>(url: string, data?: P, config?: AxiosRequestConfig): Promise<IResponseData<R>> => httpNative.post<P, IResponseData<R>>(url, data, config),
  put: <P, R>(url: string, data?: P, config?: AxiosRequestConfig): Promise<IResponseData<R>> => httpNative.put<P, IResponseData<R>>(url, data, config)
}

const httpNative = {
  get: <P, R>(url: string, param?: P, config?: AxiosRequestConfig): Promise<R> => new Promise<R>((resolve, reject) => {
    const paramStr = qs.stringify(param)
    instance.get(`${url}?${paramStr}`, config).then((res) => {
      !res.data && reject(res)
      res.data.success ? resolve(res.data as R) : reject(res.data as R)
    }).catch(err => reject(err))
  }),
  delete: <P, R>(url: string, param?: P, config?: AxiosRequestConfig): Promise<R> => new Promise<R>((resolve, reject) => {
    const paramStr = qs.stringify(param)
    instance.delete(`${url}?${paramStr}`, config).then((res) => {
      !res.data && reject(res)
      res.data.success ? resolve(res.data as R) : reject(res.data as R)
    }).catch(err => reject(err))
  }),
  post: <P, R>(url: string, data?: P, config?: AxiosRequestConfig): Promise<R> => new Promise<R>((resolve, reject) => {
    instance.post(url, data, config).then((res) => {
      !res.data && reject(res)
      res.data.success ? resolve(res.data as R) : reject(res.data as R)
    }).catch(err => reject(err))
  }),
  put: <P, R>(url: string, data?: P, config?: AxiosRequestConfig): Promise<R> => new Promise<R>((resolve, reject) => {
    instance.put(url, data, config).then((res) => {
      !res.data && reject(res)
      res.data.success ? resolve(res.data as R) : reject(res.data as R)
    }).catch(err => reject(err))
  })
}

/**
 * 包装响应体后的http请求
 */
export const http = httpPack
/**
 * 原生不包装的http请求
 */
export const httpNa = httpNative
